﻿/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

using Daidalos.Exception;
using Daidalos.Patterns;
using System.Xml;
using System.Windows.Forms;

namespace Daidalos
{

    public sealed class Engine : SingletonManager<Engine>
    {

        #region Attributes
        /// <summary>
        ///    The width of the client area.
        /// </summary>
        private int clientWidth = -1;

        /// <summary>
        ///    The height of the client area.
        /// </summary>
        private int clientHeight = -1;

        /// <summary>
        ///    Is the client started in windowed mode?
        /// </summary>
        private bool windowedMode = true;

        private int tmp = 10;
        private string test = "hi there";
        #endregion

        #region Methods
        /// <summary>
        ///    Causes the engine to load it's main configuration
        ///    stored in the supplied file.
        /// </summary>
        /// <param name="clientConfig">
        ///    The path to the client configuration file.
        /// </param>
        public void Configure(string clientConfig)
        {
            // Verify arguments.
            if (clientConfig == null || clientConfig == "")
            {
                throw new InvalidArgumentException("Tried to create an engine from an invalid client configuration file!");
            }

            // Parse the configuration.
            ParseClientConfiguration(clientConfig);
        }

        /// <summary>
        ///    This method will invoke the state machine, which then
        ///    will take control on the information and rendering flow
        ///    of the engine.
        /// </summary>
        public void InvokeStateMachine()
        {
            try
            {
                while (Daidalos.Graphics.RenderDevice.Instance.IsGood())
                {
                    // Process events.
                    Application.DoEvents();

                    // Render the current frame.
                    Daidalos.Graphics.RenderDevice.Instance.Render();

                    // Poll input peripherals.
                    Daidalos.Input.Peripherals.Instance.PollInputPeripherals();

                    // Close on escape.
                    if (Daidalos.Input.Peripherals.Instance.EscapePressed)
                    {
                        Daidalos.Graphics.RenderDevice.Instance.RequestTermination();
                    }
                }
            }
            catch (System.Exception e)
            {
                System.Windows.Forms.MessageBox.Show(e.ToString());
            }
        }

        /// <summary>
        ///    This method parses the client configuration and initializes
        ///    the engine according to the information from that configuration.
        /// </summary>
        /// <param name="clientConfig">
        ///    The path to the client configuration file.
        /// </param>
        private void ParseClientConfiguration(string clientConfig)
        {
            // Fetch a XML document.
            XmlDocument xml = new XmlDocument();
            if (xml == null)
            {
                throw new GenericException("Failed to create a XmlDocument-object!");
            }

            // Try to load the client configuration.
            xml.Load(clientConfig);

            // Try to fetch the root element.
            XmlElement root = xml.DocumentElement;
            if (root == null || !root.HasChildNodes)
            {
                throw new GenericException("Incomplete or damaged client configuration!");
            }

            // Try to fetch the client node.
            XmlNode screen = root.SelectSingleNode("screen");
            if (screen == null)
            {
                throw new GenericException("Client configuration lacks the client-tag!");
            }

            // Extract the client size.
            if (screen.Attributes["width"] == null || screen.Attributes["height"] == null || screen.Attributes["windowed"] == null)
            {
                throw new GenericException("Client-tag damaged or incomplete!");
            }
            clientWidth = int.Parse(screen.Attributes["width"].Value);
            clientHeight = int.Parse(screen.Attributes["height"].Value);
            windowedMode = bool.Parse(screen.Attributes["windowed"].Value);
        }

        /// <summary>
        ///    Adds an initializer to the engine.
        /// </summary>
        /// <param name="initializer">
        ///    A delegate for the initializer.
        /// </param>
        public void AddInitializer(Daidalos.InitializerDelegate initializer)
        {
            // Pass the initializer function to the rendering device.
            Graphics.RenderDevice.Instance.AddExternalInitializer(initializer);
        }

        /// <summary>
        ///    Prepares the engine to start running.
        /// </summary>
        public void Initialize()
        {
            // Create the rendering device.
            Graphics.RenderDevice.Instance.CreateDevice(clientWidth, clientHeight, !windowedMode);
        }

        /// <summary>
        ///    Cleans up resources allocated by the engine.
        /// </summary>
        public void Dipose()
        {
            // Cleanup the engine's resources.
            Graphics.RenderDevice.Instance.Dispose();
        }

        /// <summary>
        ///    Registers a texture search path.
        /// </summary>
        /// <param name="path">
        ///    The search path for the textures.
        /// </param>
        public void AddTexturePath(string path)
        {
            // Add the path to the resource manager.
            Management.ResourceManager.Instance.AddTextureSearchPath(path);
        }

        /// <summary>
        ///    Unregisters a texture search path.
        /// </summary>
        /// <param name="path">
        ///    The search path for the textures.
        /// </param>
        public void RemoveTexturePath(string path)
        {
            // Remove the path from the resource manager.
            Management.ResourceManager.Instance.RemoveTextureSearchPath(path);
        }
        #endregion

    }

}
